{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright 2018 Google LLC  \n",
    "  \n",
    " Licensed under the Apache License, Version 2.0 (the \"License\");  \n",
    " you may not use this file except in compliance with the License.  \n",
    " You may obtain a copy of the License at  \n",
    "  \n",
    "     http://www.apache.org/licenses/LICENSE-2.0  \n",
    "  \n",
    " Unless required by applicable law or agreed to in writing, software  \n",
    " distributed under the License is distributed on an \"AS IS\" BASIS,  \n",
    " WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  \n",
    " See the License for the specific language governing permissions and  \n",
    " limitations under the License."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Only for when running on Colab:\n",
    "import sys\n",
    "if 'google.colab' in sys.modules:\n",
    "    # Get the dependency .py files, if any.\n",
    "    ! git clone https://github.com/GoogleCloudPlatform/cloudml-samples.git\n",
    "    ! cp cloudml-samples/tpu/templates/tpu_rewrite/* .\n",
    "\n",
    "    # Authenticate the user for better GCS access.\n",
    "    # Copy verification code into the text field to continue.\n",
    "    from google.colab import auth\n",
    "    auth.authenticate_user()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import argparse\n",
    "import numpy as np\n",
    "import os\n",
    "import tensorflow as tf\n",
    "from tensorflow.contrib.cluster_resolver import TPUClusterResolver"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def tpu_computation(features, labels):\n",
    "    # Similar to the role of model_fn, the TPU function builds the part of the graph to be run on TPUs\n",
    "\n",
    "    # build model\n",
    "    hidden = tf.layers.dense(features, 10, activation=tf.nn.relu)\n",
    "    outputs = tf.layers.dense(hidden, 1)\n",
    "    loss = tf.nn.l2_loss(outputs - labels)\n",
    "\n",
    "    optimizer = tf.train.RMSPropOptimizer(learning_rate=0.05)\n",
    "\n",
    "    # Wrap the optimizer\n",
    "    optimizer = tf.contrib.tpu.CrossShardOptimizer(optimizer)\n",
    "\n",
    "    global_step = tf.train.get_or_create_global_step()\n",
    "    train_op = optimizer.minimize(loss, global_step=global_step) \n",
    "\n",
    "    # TPU functions must return zero-or more Tensor values followed by zero or more Operations.\n",
    "    return global_step, loss, train_op"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_input_fn():\n",
    "    # data input function runs on the CPU, not TPU\n",
    "\n",
    "    # make some fake regression data\n",
    "    x = np.random.rand(100, 5)\n",
    "    w = np.random.rand(5)\n",
    "    y = np.sum(x * w, axis=1)\n",
    "\n",
    "    # TPUs currently do not support float64\n",
    "    x_tensor = tf.constant(x, dtype=tf.float32)\n",
    "    y_tensor = tf.constant(y, dtype=tf.float32)\n",
    "\n",
    "    # create tf.data.Dataset\n",
    "    dataset = tf.data.Dataset.from_tensor_slices((x_tensor, y_tensor))\n",
    "\n",
    "    # TPUs need to know all dimensions including batch size\n",
    "    batch_size = 16\n",
    "\n",
    "    dataset = dataset.repeat().shuffle(32).batch(batch_size)#, drop_remainder=True)\n",
    "\n",
    "    # TPUs need to know all dimensions when the graph is built\n",
    "    # Datasets know the batch size only when the graph is run\n",
    "    def set_shapes(features, labels):\n",
    "        features_shape = features.get_shape().merge_with([batch_size, None])\n",
    "        labels_shape = labels.get_shape().merge_with([batch_size])\n",
    "\n",
    "        features.set_shape(features_shape)\n",
    "        labels.set_shape(labels_shape)\n",
    "\n",
    "        return features, labels\n",
    "\n",
    "    dataset = dataset.map(set_shapes)\n",
    "    dataset = dataset.prefetch(tf.contrib.data.AUTOTUNE)\n",
    "\n",
    "    return dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def main(args):\n",
    "    # unpack the tensor batch to be used as the list of inputs of the TPU function\n",
    "    dataset = train_input_fn()\n",
    "    iterator = dataset.make_one_shot_iterator()\n",
    "    features, labels = iterator.get_next()\n",
    "\n",
    "    # mark part of the graph to be run on the TPUs\n",
    "    global_step_tensor, loss_tensor = tf.contrib.tpu.rewrite(tpu_computation, [features, labels])\n",
    "\n",
    "    # utility ops\n",
    "    tpu_init = tf.contrib.tpu.initialize_system()\n",
    "    tpu_shutdown = tf.contrib.tpu.shutdown_system()\n",
    "    variables_init = tf.global_variables_initializer()\n",
    "\n",
    "    saver = tf.train.Saver()\n",
    "\n",
    "    # get the TPU resource's grpc url\n",
    "    # Note: when running on AI Platform, args.tpu should be left as None\n",
    "    tpu_grpc_url = TPUClusterResolver(tpu=args.tpu).get_master()\n",
    "    sess = tf.Session(tpu_grpc_url)\n",
    "\n",
    "    sess.run(tpu_init)\n",
    "    sess.run(variables_init)\n",
    "\n",
    "    for i in range(args.max_steps):\n",
    "        # the tensor values in the TPU function are returned in a list, and the operations in the TPU function are called with no return value\n",
    "        global_step, loss = sess.run([global_step_tensor, loss_tensor])\n",
    "\n",
    "        if i % args.save_checkpoints_steps == 0:\n",
    "            saver.save(sess, os.path.join(args.model_dir, 'model.ckpt'), global_step=global_step)\n",
    "\n",
    "            tf.logging.info('global_step: {}, loss: {}'.format(global_step, loss))\n",
    "\n",
    "    sess.run(tpu_shutdown)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "parser = argparse.ArgumentParser()\n",
    "\n",
    "parser.add_argument(\n",
    "    '--model-dir',\n",
    "    type=str,\n",
    "    default='/tmp/tpu-template',\n",
    "    help='Location to write checkpoints and summaries to.  Must be a GCS URI when using Cloud TPU.')\n",
    "parser.add_argument(\n",
    "    '--max-steps',\n",
    "    type=int,\n",
    "    default=1000,\n",
    "    help='The total number of steps to train the model.')\n",
    "parser.add_argument(\n",
    "    '--save-checkpoints-steps',\n",
    "    type=int,\n",
    "    default=100,\n",
    "    help='The number of training steps before saving each checkpoint.')\n",
    "parser.add_argument(\n",
    "    '--tpu',\n",
    "    default=None,\n",
    "    help='The name or GRPC URL of the TPU node.  Leave it as `None` when training on AI Platform.')\n",
    "\n",
    "args, _ = parser.parse_known_args()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# colab.research.google.com specific\n",
    "if 'google.colab' in sys.modules:\n",
    "    import json\n",
    "    import os\n",
    "\n",
    "    # TODO(user): change this\n",
    "    args.model_dir = 'gs://your-gcs-bucket'\n",
    "\n",
    "    # When connected to the TPU runtime\n",
    "    if 'COLAB_TPU_ADDR' in os.environ:\n",
    "        tpu_grpc = 'grpc://{}'.format(os.environ['COLAB_TPU_ADDR'])\n",
    "\n",
    "        args.tpu = tpu_grpc\n",
    "        args.use_tpu = True\n",
    "\n",
    "        # Upload credentials to the TPU\n",
    "        with tf.Session(tpu_grpc) as sess:\n",
    "            data = json.load(open('/content/adc.json'))\n",
    "            tf.contrib.cloud.configure_gcs(sess, credentials=data)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "main(args)"
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 2
}
